home *** CD-ROM | disk | FTP | other *** search
- /*
- * object.c
- *
- * Copyright (C) 1989, 1991, Craig E. Kolb
- * All rights reserved.
- *
- * This software may be freely copied, modified, and redistributed
- * provided that this copyright notice is preserved on all copies.
- *
- * You may not distribute this software, in whole or in part, as part of
- * any commercial product without the express consent of the authors.
- *
- * There is no warranty or other guarantee of fitness of this software
- * for any purpose. It is provided solely "as is".
- *
- * $Id: geom.c,v 4.0.1.1 91/09/29 15:43:15 cek Exp Locker: cek $
- *
- * $Log: geom.c,v $
- * Revision 4.0.1.1 91/09/29 15:43:15 cek
- * patch1: GeomBounds now inflates bounds by EPSILON.
- *
- * Revision 4.0 91/07/17 14:37:47 kolb
- * Initial version.
- *
- */
- #include "geom.h"
- #include "list.h"
- #include "sampling.h"
-
- static void GeomBounds(), GeomBoundsAnimated();
- void GeomResolveAssoc(); /* probably static */
-
- Geom *
- GeomCreate(objptr, methods)
- GeomRef objptr;
- Methods *methods;
- {
- Geom *obj;
-
- if (objptr == (GeomRef)NULL)
- return (Geom *)NULL;
-
- obj = (Geom *)share_calloc(1, sizeof(Geom));
- obj->obj = objptr;
- obj->methods = methods;
- obj->animtrans = FALSE;
- obj->trans = obj->transtail = (Trans *) NULL;
- obj->frame = -1; /* impossible value */
- BoundsInit(obj->bounds);
- #ifdef SHAREDMEM
- /*
- * If the counter is in shared memory, processes will
- * be modifying it left-and-right. So, we cheat and
- * make counter a pointer to a non-shared location and
- * store the value there.
- */
- new->counter = (unsigned long *)RayMalloc(sizeof(unsigned long));
- *new->counter = 0;
- #endif
- return obj;
- }
-
- /*
- * Return a copy of the given object.
- * Note that surface, texturing, and transformation information
- * is copied by reference.
- */
- Geom *
- GeomCopy(obj)
- Geom *obj;
- {
- Geom *new;
-
- new = GeomCreate(obj->obj, obj->methods);
- /* Share texturing, name, #prims, surface info */
- new->name = obj->name;
- new->texture = obj->texture;
- new->surf = obj->surf;
- new->prims = obj->prims;
- new->trans = obj->trans;
- new->animtrans = obj->animtrans;
- new->transtail = obj->transtail;
- /* copy bounds */
- BoundsCopy(obj->bounds, new->bounds);
- return new;
- }
-
- /*
- * Report bounding box and number of primitives in object.
- */
- void
- AggregatePrintInfo(obj, fp)
- Geom *obj;
- FILE *fp;
- {
- if (fp) {
- if (obj->name && obj->name[0])
- fprintf(fp,"%s \"%s\":\n", GeomName(obj), obj->name);
- else
- fprintf(fp,"%s:\n", GeomName(obj));
- if (!UNBOUNDED(obj))
- BoundsPrint(obj->bounds, fp);
- fprintf(fp,"\t%lu primitive%c\n",obj->prims,
- obj->prims == 1 ? ' ' : 's');
- }
- }
-
- /*
- * Convert the given object from a linked list of objects to
- * the desired aggregate type.
- */
- int
- AggregateConvert(obj, objlist)
- Geom *obj, *objlist;
- {
- if (!IsAggregate(obj)) {
- RLerror(RL_ABORT, "A %s isn't an aggregate.\n",
- GeomName(obj));
- return 0;
- }
-
- return (*obj->methods->convert)(obj->obj, objlist);
- }
-
- /*
- * This should really be called
- * GeomInitialize
- * or something.
- */
- void
- GeomComputeBounds(obj)
- Geom *obj;
- {
- if (obj->frame == Sampling.framenum)
- return;
-
- if (!obj->animtrans) {
- /*
- * If it isn't animated,
- * just compute bbox directly
- */
- GeomBounds(obj, obj->bounds);
- } else {
- /*
- * Animated things are gonna get a bbox
- * which is large enough to enclose all
- * the places where the object goes.
- */
- GeomBoundsAnimated(obj);
- }
- /*
- * Enlarge by EPSILON in each direction just to
- * be on the safe side.
- */
- obj->bounds[LOW][X] -= EPSILON;
- obj->bounds[HIGH][X] += EPSILON;
- obj->bounds[LOW][Y] -= EPSILON;
- obj->bounds[HIGH][Y] += EPSILON;
- obj->bounds[LOW][Z] -= EPSILON;
- obj->bounds[HIGH][Z] += EPSILON;
- /*
- * Mark the fact that that the obj is initialized
- * for this frame.
- */
- obj->frame = Sampling.framenum;
- obj->counter = 0;
- }
-
- static void
- GeomBoundsAnimated(obj)
- Geom *obj;
- {
- int i, m;
- Float newbounds[2][3];
- Float window, subwindow, jitter, subjitter;
-
- /*
- * For each possible screen sample,
- * choose TIME_SUB_SAMPLES times and recompute the
- * bounds of obj at that time,
- * expanding the computed bounding box appropriately.
- */
- BoundsInit(obj->bounds);
- jitter = Sampling.shutter / Sampling.totsamples;
- subjitter = jitter / (Float)TIME_SUB_SAMPLES;
- window = Sampling.starttime;
- for (i = 0; i < Sampling.totsamples; i++, window += jitter) {
- subwindow = window;
- for (m = 0; m < TIME_SUB_SAMPLES; m++, subwindow += subjitter) {
- /*
- * Set the current time.
- */
- TimeSet(subwindow + subjitter*nrand());
- /*
- * Resolve the objects geometric associations
- */
- GeomResolveAssoc(obj);
- /*
- * Compute bounds and expand current bounds.
- */
- GeomBounds(obj, newbounds);
- BoundsEnlarge(obj->bounds, newbounds);
- }
- }
- /*
- * Also sample at time extremes, as for many
- * movements, extremes occur at beginning/end times.
- */
- TimeSet(Sampling.starttime);
- GeomResolveAssoc(obj);
- GeomBounds(obj, newbounds);
- BoundsEnlarge(obj->bounds, newbounds);
-
- TimeSet(Sampling.starttime + Sampling.shutter);
- GeomResolveAssoc(obj);
- GeomBounds(obj, newbounds);
- BoundsEnlarge(obj->bounds, newbounds);
- }
-
- void
- GeomResolveAssoc(obj)
- Geom *obj;
- {
- /*
- * PrimResolveAssoc(obj);
- */
- TransResolveAssoc(obj->trans);
- }
-
- /*
- * Set "bounds" of object to be the extent of the primitive.
- */
- static void
- GeomBounds(obj, bounds)
- Geom *obj;
- Float bounds[2][3];
- {
- Trans *trans;
-
- if (!obj || !obj->methods->bounds)
- RLerror(RL_ABORT, "Can't compute bounds of \"%s\".\n",
- GeomName(obj));
- (*obj->methods->bounds) (obj->obj, bounds);
- bounds[LOW][X] -= EPSILON;
- bounds[LOW][Y] -= EPSILON;
- bounds[LOW][Z] -= EPSILON;
- bounds[HIGH][X] += EPSILON;
- bounds[HIGH][Y] += EPSILON;
- bounds[HIGH][Z] += EPSILON;
- if (obj->trans) {
- for (trans = obj->trans; trans; trans = trans->next)
- BoundsTransform(&trans->trans, bounds);
- }
- }
-
- char *
- GeomName(obj)
- Geom *obj;
- {
- if (obj->methods->name)
- return (*obj->methods->name)();
-
- return "unknown";
- }
-
- void
- GeomStats(obj, tests, hits)
- Geom *obj;
- unsigned long *tests, *hits;
- {
- if (obj && obj->methods->stats)
- (*obj->methods->stats)(tests, hits);
- else {
- *tests = *hits = 0;
- }
- }
-
- /*
- * Push an object onto the head of the given stack, returning
- * the new head.
- */
- GeomList *
- GeomStackPush(obj, list)
- Geom *obj;
- GeomList *list;
- {
- GeomList *new;
- /*
- * Pretty simple.
- * Make new element point to old head and return new head.
- */
- new = (GeomList *)RayMalloc(sizeof(GeomList));
- new->obj = obj;
- new->next = list;
- return new;
- }
-
- /*
- * Pop the topmost object off of the given stack, returning the new head.
- * The old head is freed, but the object it points to is not.
- */
- GeomList *
- GeomStackPop(list)
- GeomList *list;
- {
- GeomList *ltmp;
-
- ltmp = list->next; /* Save new head. */
- free((voidstar)list); /* Free old head. */
- return ltmp; /* Return new head. */
- }
-
- Methods *
- MethodsCreate()
- {
- return (Methods *)share_calloc(1, sizeof(Methods));
- }
-
- /*
- * Call appropriate routine to compute UV and, if non-null,
- * dpdu and dpdv at given point on the given primitive. The
- * normal is used to facilitate computation of u, v, and the
- * partial derivatives.
- */
- void
- PrimUV(prim, pos, norm, uv, dpdu, dpdv)
- Geom *prim;
- Vector *pos, *norm, *dpdu, *dpdv;
- Vec2d *uv;
- {
- /*
- * Call appropriate inverse mapping routine
- */
- if (prim->methods->uv == NULL) {
- uv->u = uv->v = 0.;
- if (dpdu) {
- dpdu->y = dpdu->z = 0.;
- dpdu->x = 1.;
- }
- if (dpdv) {
- dpdv->x = dpdv->z = 0.;
- dpdv->y = 1.;
- }
- } else
- (*prim->methods->uv)(prim->obj,pos,norm,uv,dpdu,dpdv);
- }
-
- int
- PrimNormal(prim, pos, norm, gnorm)
- Geom *prim;
- Vector *pos, *norm, *gnorm;
- {
- /*
- * Call appropriate normal routine
- */
- return (*prim->methods->normal) (prim->obj, pos, norm, gnorm);
- }
-
- int
- PrimEnter(obj, ray, mind, hitd)
- Geom *obj;
- Ray *ray;
- Float mind, hitd;
- {
- /*
- * Call appropriate enter/leave routine
- */
- if (obj->methods->enter == NULL) {
- Vector pos, nrm, gnrm;
- /*
- * Sleazy method: Use hit point, find normal
- * and take dot prod with ray
- */
- VecAddScaled(ray->pos, hitd, ray->dir, &pos);
- PrimNormal(obj, &pos, &nrm, &gnrm);
-
- return dotp(&ray->dir, &gnrm) < 0.0;
- }
- else
- return (*obj->methods->enter) (obj->obj, ray, mind, hitd);
- }
-
- /*
- * Walk through a linked-list of objects. If the object is unbounded,
- * unlink it it from the list and add it to the 'unbounded' list.
- * If the object is bounded, enlarge the given bounding box if
- * necessary. Return pointer to unbounded list.
- */
- Geom *
- GeomComputeAggregateBounds(bounded, unbounded, bounds)
- Geom **bounded, *unbounded;
- Float bounds[2][3];
- {
- Geom *ltmp, *prev, *nextobj;
-
- BoundsInit(bounds);
-
- prev = (Geom *)0;
-
- for (ltmp = *bounded; ltmp; ltmp = nextobj) {
- nextobj = ltmp->next;
- GeomComputeBounds(ltmp);
- if (UNBOUNDED(ltmp)) {
- /*
- * Geom is unbounded -- unlink it...
- */
- if (prev)
- prev->next = ltmp->next;
- else
- *bounded = ltmp->next;
- /*
- * And add it to unbounded object list.
- */
- ltmp->next = unbounded;
- unbounded = ltmp;
- } else {
- /*
- * Geom is bounded.
- */
- BoundsEnlarge(bounds, ltmp->bounds);
- prev = ltmp;
- }
- }
- return unbounded;
- }
-
- /*
- * Find 'highest' animated object on the hitlist.
- */
- int
- FirstAnimatedGeom(hitlist)
- HitList *hitlist;
- {
- int i;
-
- for (i = hitlist->nodes -1; i; i--)
- /*
- * If object itself is animated, have
- * to check other flag, too...
- */
- if (hitlist->data[i].obj->animtrans)
- return i;
- return 0;
- }
-